package security.core.source.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import security.debug.RBACLogger;

public abstract class RoleSourceService {
	private Map<String, Set<String>> rolePerssionsMap;
	private Map<String, Set<String>> rawRolePerssionsMap;
	private Map<String, Set<String>> roleHierarchyMap;
	
	public RoleSourceService() {
		if (RBACLogger.debug()) {
			RBACLogger.log("RoleSourceService#RoleSourceService is called!");
		}
		this.roleHierarchyMap = loadRoleHierarchy();
		this.rawRolePerssionsMap = loadRawPermissions();
		this.rolePerssionsMap = loadPermissions();
	}
	
	public Map<String, Set<String>> getRoleHierarchyMap() {
		return this.roleHierarchyMap;
	}
	
	public Map<String, Set<String>> getRawRolePermissionsMap() {
		return this.rawRolePerssionsMap;
	}
	
	public Map<String, Set<String>> getRolePermissionsMap() {
		return this.rolePerssionsMap;
	}
	
	private Map<String, Set<String>> loadPermissions() {
		if (RBACLogger.debug()) {
			RBACLogger.log("RoleSourceService#loadPermissions is called!");
		}
		Map<String, Set<String>> rpMap = new HashMap<String, Set<String>>();
		List<String> orderedRole = new ArrayList<String>();
		Map<String, Set<String>> partialOrderRole = new HashMap<String, Set<String>>(roleHierarchyMap);

		// get topological order list of role from the partial ordered role map
		while (!partialOrderRole.isEmpty()) {
			for (String role : roleHierarchyMap.keySet()) {
				if (orderedRole.contains(role)) {
					continue;
				} else if (orderedRole.containsAll(partialOrderRole.get(role))) {
					orderedRole.add(role);
					partialOrderRole.remove(role);
				}
			}
		}
		
		// construct the role permission map according to the topological ordered role list
		for (String role : orderedRole) {
			Set<String> permissions = new HashSet<String>();
			Set<String> parents = roleHierarchyMap.get(role);
			permissions.addAll(this.rawRolePerssionsMap.get(role));
			for (String parentRole : parents) {
				permissions.addAll(rpMap.get(parentRole));
			}
			rpMap.put(role, permissions);
			
		}
		return rpMap;
	}

	/*
	 * the role is a partial order set, the map key represents the role name and the set value represents
	 * the direct parent of the role of the map key.
	 */
	public abstract Map<String, Set<String>> loadRoleHierarchy();
	public abstract Map<String, Set<String>> loadRawPermissions();
}
